package mspbots.tsheets.client.core.users;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.tsheets.client.core.AbstractToolsUtil;
import mspbots.tsheets.client.core.BaseSyncService;
import mspbots.tsheets.client.core.TokenState;
import mspbots.tsheets.common.*;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.Map;


/**
 * @author AlexBob
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class UsersServiceImpl extends AbstractToolsUtil implements BaseSyncService {

    @Override
    public Mono<Void> syncFast(TokenState tokenState) {
        ResultSync resultSync = ResultSync.withDefault(tokenState.getTenantId(), "USERS-SYNC");
        return this.filterCacheGet(tokenState)
                .doOnNext(resultNode -> this.sendEntity(RabbitKeys.USERS_SYNC_QUEUE_KEY, ResultMsg.builder()
                        .requestId(resultSync.getRequestId()).id(resultNode.get("id").longValue())
                        .action("none").entity(resultNode).tenantId(tokenState.getTenantId())
                        .syncDate(LocalDateTime.now()).build()))
                .subscriberContext(ctx -> ctx.put("resultSync", resultSync))
                .then();
    }

    private Flux<JsonNode> filterCacheGet(TokenState tokenState) {
        String anchorTimeKey = RedisKeys.USERS_ANCHOR_REDIS_KEY_PREFIX + tokenState.getTenantId();
        return this.readAnchorTime(anchorTimeKey)
                .map(time -> QueryParams.withDefault().setModifiedSince(time.minusYears(20)))
                .flatMapMany(this::get)
                .subscriberContext(ctx -> ctx.put("token", tokenState))
                .doOnNext(jsonNode -> this.recordAnchorTime(anchorTimeKey, jsonNode.get("last_modified").textValue()));
    }

    private Flux<JsonNode> get(QueryParams queryParams) {
        queryParams.set("active", "both");
        queryParams.set("supplemental_data", "no");
        return super.sheetsClient.fetch("/users", queryParams)
                .map(usersNode -> this.objectMapper.convertValue(usersNode.get("users"), new TypeReference<Map<String, JsonNode>>() {
                }))
                .flatMap(mapUser -> Flux.fromStream(mapUser.values().parallelStream()));
    }

/*    public void autoSyncUsers(String syncId, Long tenantId, int page) {
        final long start = System.nanoTime();
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.set("supplemental_data", "no");
        params.set("page", String.valueOf(page));

        sheetsClient.getUserRepository(tenantId, params)
                .subscribe(result -> {
                    Map<String, ObjectNode> users = this.objectMapper.convertValue(result.findPath("results").findPath("users"),
                            new TypeReference<>() {
                            });
                    users.forEach((k, v) -> {
                        SheetsUser sheetsUser = new SheetsUser();
                        sheetsUser.setEmail(v.findValue("email").asText());
                        sheetsUser.setSheetUserId(Long.parseLong(k));
                        sheetsUser.setFirstName(v.findValue("first_name").asText());
                        sheetsUser.setLastName(v.findValue("last_name").asText());
                        sheetsUser.setLastModified(super.timeCovert(v.findValue("last_modified").asText()));
                        sheetsUser.setCreated(super.timeCovert(v.findValue("created").asText()));
                        sheetsUser.setTenantId(tenantId);
                        sheetsUser.setSyncTime(LocalDateTime.now());
                        sheetsUser.setExtend(v);

                        this.messagingTemplate.convertAndSend(RabbitConfiguration.MQ_EXCHANGE, "timesheet-users-key", sheetsUser);
                    });

                    if (result.findValue("more").asBoolean()) {
                        int pageNow = page + 1;
                        this.autoSyncUsers(syncId, tenantId, pageNow);
                    }
                    long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                    log.debug("Time taken {}: {}  milliseconds.", "Completed", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
                    this.messagingTemplate.convertAndSend("timesheet.sync.time", SyncTimeDTO.builder()
                            .api(API.V1_SHEETS_USER)
                            .last_time(LocalDateTime.now())
                            .milliseconds(milliseconds)
                            .status("Completed")
                            .msg("Success")
                            .tenantId(tenantId)
                            .build()
                    );

                    this.sendLog(syncId, tenantId, milliseconds, API.V1_SHEETS_USER, SUCCESS_CODE, "success", result);

                }, err -> {
                    long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                    log.error("Companies sync data error, msg:{}", err.getMessage());
                    this.messagingTemplate.convertAndSend("timesheet.sync.time", SyncTimeDTO.builder()
                            .api(API.V1_SHEETS_USER)
                            .last_time(LocalDateTime.now())
                            .milliseconds(milliseconds)
                            .status("Error")
                            .msg(err.getMessage())
                            .tenantId(tenantId)
                            .build()
                    );


                    this.sendLog(syncId, tenantId, milliseconds, API.V1_SHEETS_USER, FAIL_CODE, err.getMessage(), null);
                });
    }*/

}
